assignManipulator = $assignManipulator; $this->reflectionResolver = $reflectionResolver; $this->nodeNameResolver = $nodeNameResolver; $this->collectionVarTagValueNodeResolver = $collectionVarTagValueNodeResolver; $this->staticTypeMapper = $staticTypeMapper; $this->collectionTypeFactory = $collectionTypeFactory; } public function resolveAssignedGenericCollectionType(Class_ $class, ClassMethod $classMethod) : ?GenericObjectType { $propertyFetches = $this->assignManipulator->resolveAssignsToLocalPropertyFetches($classMethod); if (\count($propertyFetches) !== 1) { return null; } $phpPropertyReflection = $this->reflectionResolver->resolvePropertyReflectionFromPropertyFetch($propertyFetches[0]); if (!$phpPropertyReflection instanceof PhpPropertyReflection) { return null; } $propertyName = (string) $this->nodeNameResolver->getName($propertyFetches[0]); $property = $class->getProperty($propertyName); if (!$property instanceof Property) { return null; } $varTagValueNode = $this->collectionVarTagValueNodeResolver->resolve($property); if (!$varTagValueNode instanceof VarTagValueNode) { return null; } // remove collection union type, so this can be turned into generic type $resolvedType = $this->staticTypeMapper->mapPHPStanPhpDocTypeNodeToPHPStanType($varTagValueNode->type, $property); if ($resolvedType instanceof UnionType) { $nonCollectionTypes = []; foreach ($resolvedType->getTypes() as $unionedType) { if (!$this->isCollectionType($unionedType)) { continue; } $nonCollectionTypes[] = $unionedType; } if (\count($nonCollectionTypes) === 1) { $soleType = $nonCollectionTypes[0]; if ($soleType instanceof ArrayType && $soleType->getItemType() instanceof ObjectType) { return $this->collectionTypeFactory->createType($soleType->getItemType()); } } } if ($resolvedType instanceof GenericObjectType) { return $resolvedType; } return null; } private function isCollectionType(Type $type) : bool { if ($type instanceof ShortenedObjectType && $type->getFullyQualifiedName() === DoctrineClass::COLLECTION) { return \true; } return $type instanceof ObjectType && $type->getClassName() === DoctrineClass::COLLECTION; } }